package org.leviatan.common.xml;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringBufferInputStream;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * @author Leviatan
 * @version 0.4
 */
public class XMLIO {
    
    private static XMLIO instance;
    
    private static boolean validating = false;
    
    private static boolean nameSpaceAware = true;
    
    private XMLIO() { }
    
    public static XMLIO getInstance() {
        if (XMLIO.instance == null) {
            XMLIO.instance = new XMLIO();
        }
        return instance;
    }
    
    public boolean isNameSpaceAware() {
        return XMLIO.nameSpaceAware;
    }
    
    public void setNameSpaceAware(boolean nameSpaceAware) {
        XMLIO.nameSpaceAware = nameSpaceAware;
    }
    
    public boolean isValidating() {
        return XMLIO.validating;
    }
    
    public void setValidating(boolean validating) {
        XMLIO.validating = validating;
    }
    
    public void writeDocumentToXMLFile(Document doc, String filePath) {
        
        TransformerFactory tFactory = TransformerFactory.newInstance();
        try {
            
            Transformer transformer = tFactory.newTransformer();
            
            if (doc.getDoctype() != null) {
                String publicValue = doc.getDoctype().getPublicId();
                String systemValue = doc.getDoctype().getSystemId();
                
                transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicValue);
                transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemValue);
            }
            
            File file = new File(filePath);
            try {
                                
                FileOutputStream fos = new FileOutputStream(file, false);
                BufferedOutputStream bos = new BufferedOutputStream(fos);
                
                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(bos);
                transformer.transform(source, result);
                
                fos.flush();
                fos.close();
                
                bos.flush();
                bos.close();
                
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            catch (IOException ioe) { 
                ioe.printStackTrace();
            }
            
        } catch (TransformerConfigurationException tce) {
            // Error generated by the parser
            System.out.println ("* Transformer Factory error");
            System.out.println("  " + tce.getMessage() );
    
             // Use the contained exception, if any
            Throwable x = tce;
            if (tce.getException() != null)
                x = tce.getException();
            x.printStackTrace();
       
        } catch (TransformerException te) {
            // Error generated by the parser
            System.out.println ("* Transformation error");
            System.out.println("  " + te.getMessage() );
    
            // Use the contained exception, if any
            Throwable x = te;
            if (te.getException() != null)
                x = te.getException();
            x.printStackTrace();
            
        }    
    }
    
    public void writeDocumentToConsole(Document doc) {
        
        TransformerFactory tFactory = TransformerFactory.newInstance();
        try {
            
            Transformer transformer = tFactory.newTransformer();
            
            if (doc.getDoctype() != null) {
                String publicValue = doc.getDoctype().getPublicId();
                String systemValue = doc.getDoctype().getSystemId();
                
                transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicValue);
                transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemValue);
            }
            
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(System.out);
            transformer.transform(source, result);    
            
        } catch (TransformerConfigurationException tce) {
            // Error generated by the parser
            System.out.println ("* Transformer Factory error");
            System.out.println("  " + tce.getMessage() );
    
             // Use the contained exception, if any
            Throwable x = tce;
            if (tce.getException() != null)
                x = tce.getException();
            x.printStackTrace();
       
        } catch (TransformerException te) {
            // Error generated by the parser
            System.out.println ("* Transformation error");
            System.out.println("  " + te.getMessage() );
    
            // Use the contained exception, if any
            Throwable x = te;
            if (te.getException() != null)
                x = te.getException();
            x.printStackTrace();
            
        }    
    }
    
    public Document readXMLFile(String filePath) {
        
        Document document = null;
        
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(XMLIO.validating);
        factory.setNamespaceAware(XMLIO.nameSpaceAware);

        DocumentBuilder builder = null;

        try {
            builder = factory.newDocumentBuilder();

            builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
                // ignore fatal errors (an exception is guaranteed)
                public void fatalError(SAXParseException exception) throws SAXException { }

                // treat validation errors as fatal
                public void error(SAXParseException e) throws SAXParseException { throw e; }

                // dump warnings too
                public void warning(SAXParseException err) throws SAXParseException {
                    System.out.println("** Warning" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId());
                    System.out.println("   " + err.getMessage());
                }
            });

            document = builder.parse(new File( filePath ) );

        } catch (SAXException se) {
            Exception x = se;
            if (se.getException() != null)
                x = se.getException();
            x.printStackTrace();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        
        return document;
    }
    
    public Document readXMLString(String str) {
        Document document = null;
        
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(XMLIO.validating);
        factory.setNamespaceAware(XMLIO.nameSpaceAware);
        
        //StringBufferInputStream stringInputStream = new StringBufferInputStream(str);
        StringReader stringReader = new StringReader(str);
        BufferedReader bufferedReader = new BufferedReader(stringReader);
        DocumentBuilder builder = null;

        try {
            builder = factory.newDocumentBuilder();

            builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
                // ignore fatal errors (an exception is guaranteed)
                public void fatalError(SAXParseException exception) throws SAXException { }

                // treat validation errors as fatal
                public void error(SAXParseException e) throws SAXParseException { throw e; }

                // dump warnings too
                public void warning(SAXParseException err) throws SAXParseException {
                    System.out.println("** Warning" + ", line " + err.getLineNumber() + ", uri " + err.getSystemId());
                    System.out.println("   " + err.getMessage());
                }
            });

            InputSource iS = new InputSource(bufferedReader);
            document = builder.parse( iS );
            
            
            bufferedReader.close();
            stringReader.close();

        } catch (SAXException se) {
            Exception x = se;
            if (se.getException() != null)
                x = se.getException();
            x.printStackTrace();
        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
        
        return document;
    }
    

}
